#include "bchaining.hpp"

namespace mashine { namespace inference {

using namespace mashine::kb;
using namespace boost;

// int BackwardChainingStrategy::id_counter = 0;

/**
 * param kBase - pointer to knowlege base
 */
BackwardChainingStrategy::BackwardChainingStrategy(KnowledgeBase * kBase) : InferenceStrategy()
{
	this->kBase = kBase;
	
	factSearcher = new search::FactsSearchVisitor("*");
	ruleSearcher = new search::RulesSearchVisitor("*");
	factWithMaskSearcher = new search::FactWithMaskSearchVisitor(mashine::kb::Literal());
	aRuleSearcher = new search::AnalogyRulesSearchVisitor("*");
	
	literal_map 	= get(&VertexProperties::literal, tree);
	cortege_map 	= get(&VertexProperties::facts, tree);
	is_or_node_map	= get(&VertexProperties::or_vertex, tree);
	// operation_map 	= get(&EdgeProperties::operation, tree);
}

BackwardChainingStrategy::~BackwardChainingStrategy()
{
	delete factSearcher;
	delete ruleSearcher;
	delete factWithMaskSearcher;
	delete aRuleSearcher;
	
	APP_LOGGER(info) << "BackwardChainingStrategy destructor called";
}

/**
 * backward chaining algorithm
 *
 * param goal - list of goals to inference
 */
std::vector<Literal> BackwardChainingStrategy::inference(std::vector<Literal> const & goal)
{
	APP_LOGGER(info) << "Begin inference " << utils::vector_to_string<Literal>(goal);
		
	root = add_vertex(tree);
	literal_map[root] = ~goal[0];
	is_or_node_map[root] = true;
	
	unificationAndProduction(root);
	
	print();
	
	APP_LOGGER(info) << "End inference";
	
	unionAndConcatenation(root);
	
	return cortege_map[root];
}

void BackwardChainingStrategy::unificationAndProduction(VertexDescriptor const & vd)
{
	std::vector<kb::Literal> subGoal = kBase->advanceSearch<kb::Literal, kb::Literal>(factWithMaskSearcher->setCriteria(literal_map[vd]));
	std::cout << "literals for " << literal_map[vd].toString() << " is >>> " << utils::vector_to_string<kb::Literal>(subGoal) << std::endl;

	// facts search
	//
	// vp
	// OO
	//
	// factsh attached indirect to node 
	BOOST_FOREACH(Literal const & l, subGoal) 
	{
		VertexDescriptor v1descr = add_vertex(tree);
		 literal_map[v1descr] = l;
		// cortege_map[vd].push_back(l);
		is_or_node_map[vd] = true;
		add_edge(vd, v1descr, tree);
	}
	
	std::vector<kb::Rule> rules = kBase->advanceSearch<kb::Rule, std::string>(ruleSearcher->setCriteria(literal_map[vd].getName()));
	std::cout << "rules for " << literal_map[vd].toString() << " is >>> " << mashine::utils::vector_to_string<kb::Rule>(rules) << std::endl;
	
	// rules search
	//
	//         p
	//        O 
	//       /
	// v   u/   p
	// O---O---O
	//      \
	//       \ p
	//        O
	//
	BOOST_FOREACH(Rule const & r, rules)
	{
		Rule ur = r(literal_map[vd].getParams());
		std::vector<kb::Literal> predicats = ur.getParams();

		std::cout << ur.toString() << std::endl;
		
		VertexDescriptor v1descr = add_vertex(tree);
		literal_map[v1descr] = ~(*(ur.getUndeniableLiteral()));
		is_or_node_map[root] = false;
		add_edge(vd, v1descr, tree);

		BOOST_FOREACH(Literal const & p, predicats)
		{
			if (p.isDenied())
			{
				VertexDescriptor v2descr = add_vertex(tree);
				literal_map[v2descr] = p;
				is_or_node_map[root] = true;
				add_edge(v1descr, v2descr, tree);
				
				unificationAndProduction(v2descr);
			}
		}
	}
	
	// TODO analogy rules search
}

void BackwardChainingStrategy::unionAndConcatenation(VertexDescriptor & root)
{
	// std::vector<VertexDescriptor> parent(boost::num_vertices(tree));

	// Tree t(tree, root, make_iterator_property_map(parent.begin(), 
			// get(boost::vertex_index, tree))
		// );

	// traverse_tree(root, t, union_and_projection);
	
	literalUnion(root);
}

/**
 * this method call only for OR vertex
 */ 
std::vector<mashine::kb::Literal> BackwardChainingStrategy::literalUnion(VertexDescriptor & root)
{
	std::vector<mashine::kb::Literal> result;
	
	std::pair<EdgeIterator, EdgeIterator> iPair = out_edges(root, tree);
		
	for (EdgeIterator vd = iPair.first; vd != iPair.second; ++vd)
	{
		if (literal_map[target(*vd, tree)].isFact())
		{
			result.push_back(literal_map[target(*vd, tree)]);
		}
		else
		{
			std::copy(cortege_map[target(*vd, tree)].begin(), cortege_map[target(*vd, tree)].end(), result.begin());
		}
	}
	return result;
}

/**
 * this method call only for AND vertex
 */ 
std::vector<mashine::kb::Literal> BackwardChainingStrategy::literalConcat(VertexDescriptor & root)
{
	std::pair<EdgeIterator, EdgeIterator> iPair = out_edges(root, tree);
	
	std::vector<std::map<std::string, std::string> > concat_res = concatenation_impl(iPair.first, iPair.second, 
			std::map<std::string, std::string>());
			
	std::vector<mashine::kb::Literal> result;
	std::map<std::string, std::string> m;
	BOOST_FOREACH(m, concat_res)
	{
		std::vector<std::string> params;
		BOOST_FOREACH(std::string s, literal_map[root].getParams())
		{
			params.push_back(m[s]);
		}
		
		mashine::kb::Literal l(literal_map[root].getName(), params);
		
		result.push_back(l);
	
	}
	return result;
}

/**
 * return array of maps [var_name : const_name]
 * 
 */
std::vector<std::map<std::string, std::string> > BackwardChainingStrategy::concatenation_impl(EdgeIterator & begin, EdgeIterator & end, 
		std::map<std::string, std::string> base_map)
{
	std::vector<std::map<std::string, std::string> > result;
	
	// this is the end my only frend ...
	if (begin == end)
	{
		result.push_back(base_map);
		return result;
	}
				
	bool panic = false;
	
	VertexDescriptor curr = target(*begin, tree);
	
	// iterate facts for current predicate
	BOOST_FOREACH (mashine::kb::Literal & l, cortege_map[curr]) // cortege_map[curr] call every time
	{
		std::map<std::string, std::string> curr_fact_map(base_map);

		// get variables and constants order list
		std::vector<std::string> const & vars = literal_map[curr].getParams();
		
		// test all constant current fact
		for (int i = 0; i < vars.size(); ++i)
		{
			if (utils::is_variable_name(vars[i]))
			{
				// get variable name
				std::map<std::string, std::string>::const_iterator var = curr_fact_map.find(vars[i]);
				
				if (var == curr_fact_map.end()) // if not contains in base map
				{
					curr_fact_map[vars[i]] = l.getParams()[i];
				} 
				else if (base_map[(*var).first].compare(l.getParams()[i])) // if contains other constant
				{
					panic = true;
					break;
				}
			}
		}

		if (!panic) 
		{
			++begin;

			std::vector<std::map<std::string, std::string> > p_res = 
					concatenation_impl(begin, end, curr_fact_map);

			--begin;
		
			std::copy(p_res.begin(), p_res.end(), result.end());
		}
	}

	return result;
}


// std::vector<kb::Literal> BackwardChainingStrategy::unionAndConcatenation(VertexDescriptor & root)
// {
	// std::vector<kb::Literal> result;

	// // union
	// if (is_or_node_map[root] == true)
	// {
		// std::pair<EdgeIterator, EdgeIterator> iPair = out_edges(root, tree);
		// for (EdgeIterator ei = iPair.first; ei != iPair.second; ++ei)
		// {
			// VertexDescriptor vd = target(*ei, tree);
			// std::copy(result.begin(), result.end(), unionAndConcatenation(vd).begin() );
		// }
	// }
	// else	// concatenation
	// {
		// // collect all child nodes content - Literals
		// std::pair<EdgeIterator, EdgeIterator> iPair = out_edges(root, tree);
		
		// std::vector<Literal> childs;
		// for (EdgeIterator ei = iPair.first; ei != iPair.second; ++ei)
		// {
			// childs.push_back(target(*ei, tree));
		// }
		
		// // std::vector<std::map<std::string, string> > concatenation
	// }
	
	// return result;
// }

/**
 * relation concatenation algorithm. 
 * thanks Lazurkin for idea)
 */
// std::vector<mashine::kb::Literal> concatenation(std::vector<VertexDescriptor>::const_iterator & curr, 
		// std::vector<VertexDescriptor>::const_iterator & end, std::map<std::string, string> varValue)
// {
	// std::vector<std::map<std::string, string> > result;
	
	// std::vector<kb::Literal> facts = kBase->advanceSearch<kb::Literal, kb::Literal>
			// (factWithMaskSearcher->setCriteria(literal_map[*curr]));
			
	// BOOST_FOREACH (kb::Literal & l, facts)
	// {
		// if (curr == end) return;
		
		// std::map<std::string, string> varVal();
	
		// for (int i = 0; i < (*curr).getParams().size(); ++i)
		// {
			// varVal.put((*curr).getParams()[i], l.getParams()[i]);
		// }
		
		// ++curr;
		
		// std::vector<std::map<std::string, string> > p_res = 
				// concatenation(curr, end, varVal);
				
		// --curr;
		
		// std::copy(result.begin(), p_res.begin(), p_res.end());
	// }
	
	// return result;
// }

void BackwardChainingStrategy::print()
{	
	std::vector<VertexDescriptor> parent(boost::num_vertices(tree));
	
	Tree t(tree, root, make_iterator_property_map(parent.begin(), 
			get(boost::vertex_index, tree))
		);
	
	traverse_tree(root, t, printer);
}

}}